import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

/// 滑动类型
enum DragType {
  /// 滑动更新
  update,

  /// 滑动结束
  end
}

typedef PageViewLinkBuilder = Widget Function(
    PageController, List<ScrollController>);

class PageViewLink extends StatefulWidget {
  const PageViewLink({
    Key? key,
    this.tabController,
    required this.pageController,
    required this.builder,
    this.listViewCount = 1,
  }) : super(key: key);

  final TabController? tabController;
  final PageController pageController;
  final PageViewLinkBuilder builder;
  final int listViewCount;

  @override
  State<PageViewLink> createState() => _PageViewLinkState();
}

class _PageViewLinkState extends State<PageViewLink> {
  /// 需要联动PageView的 ListView
  final List<ScrollController> _controllerList = [];

  /// 手势类型
  late Map<Type, GestureRecognizerFactory> gestures;


  late PageController _pageController;

  /// 联动 TabBar
  TabController? _tabController;

  /// 滑动类型
  DragType? _dragType;

  /// 当前拖拽对象
  Drag? _drag;

  /// 当前拖拽的 Controller
  ChangeNotifier? _active;

  /// ListView 能否联动 PageView
  bool link = false;

  @override
  void initState() {
    super.initState();

    _pageController = widget.pageController;
    _tabController = widget.tabController;

    for (int i = 0; i < widget.listViewCount; i++) {
      _controllerList.add(ScrollController());
    }

    gestures = <Type, GestureRecognizerFactory>{
      HorizontalDragGestureRecognizer:
      GestureRecognizerFactoryWithHandlers<HorizontalDragGestureRecognizer>(
              () => HorizontalDragGestureRecognizer(),
              (HorizontalDragGestureRecognizer instance) {
            instance
              ..onStart = _handleDragStart
              ..onUpdate = _handleDragUpdate
              ..onEnd = _handleDragEnd
              ..onCancel = _handleDragCancel;
          })
    };

    if (_tabController != null) {
      _pageController.addListener(linkTabs);
    }
  }

  void linkTabs() {
    /// 联动TabBar 滑动效果
    final controller = _tabController!;
    if (_dragType == DragType.update
        && !controller.indexIsChanging) {
      if ((_pageController.page! - controller.index).abs() > 1.0) {
        controller.index = _pageController.page!.round();
      }
      controller.offset =
          (_pageController.page! - controller.index).clamp(-1.0, 1.0);
    } else if (_dragType == DragType.end) {
      controller.index = _pageController.page!.round();
      if (!controller.indexIsChanging) {
        controller.offset =
            (_pageController.page! - controller.index).clamp(-1.0, 1.0);
      }
    }
  }

  @override
  void dispose() {
    super.dispose();
    for (var e in _controllerList) {
      e.dispose();
    }
    _drag?.cancel();
    _drag = null;
    _pageController.removeListener(linkTabs);
  }

  @override
  Widget build(BuildContext context) {
    return RawGestureDetector(
      gestures: gestures,
      behavior: HitTestBehavior.opaque,
      child: widget.builder(widget.pageController, _controllerList)
    );
  }

  void _handleDragStart(DragStartDetails details) {
    _dragType = null;

    ///先判断 Listview 是否可见或者可以调用
    ///一般不可见时 hasClients false ，因为 PageView 也没有 keepAlive
    _active = _controllerList.firstWhere((e) {
      if (e.hasClients == true) {
        ///获取 ListView 的  renderBox
        final RenderBox? renderBox =
        e.position.context.storageContext.findRenderObject() as RenderBox?;

        ///判断触摸的位置是否在 ListView 内
        return renderBox?.paintBounds
            .shift(renderBox.localToGlobal(Offset.zero))
            .contains(details.globalPosition) ==
            true;
      }
      return false;
    }, orElse: () => _pageController);

    /// 生成拖拽对象
    if (_active is PageController) {
      _drag = (_active as PageController).position.drag(details, _disposeDrag);
    } else if (_active is ScrollController) {
      final ScrollController controller = _active as ScrollController;
      if (controller.position.pixels >= controller.position.maxScrollExtent) {
        link = true;
      } else {
        link = false;
      }

      _drag =
          (_active as ScrollController).position.drag(details, _disposeDrag);
    }
  }

  void _handleDragUpdate(DragUpdateDetails details) {
    _dragType = DragType.update;
    if (_active is ScrollController &&

        ///手指向上移动，也就是快要显示出底部 PageView
        details.primaryDelta! < 0 &&
        link) {
      ///切换相应的控制器
      _active = _pageController;
      _drag?.cancel();

      ///参考  Scrollable 里的，
      ///因为是切换控制器，也就是要更新 Drag
      ///拖拽流程要切换到 PageView 里，所以需要  DragStartDetails
      ///所以需要把 DragUpdateDetails 变成 DragStartDetails
      ///提取出 PageView 里的 Drag 相应 details
      _drag = _pageController.position.drag(
          DragStartDetails(
              globalPosition: details.globalPosition,
              localPosition: details.localPosition),
          _disposeDrag);
    }

    _drag?.update(details);
  }

  void _handleDragEnd(DragEndDetails details) {
    _dragType = DragType.end;
    _drag?.end(details);
  }

  void _handleDragCancel() {
    _drag?.cancel();
  }

  ///拖拽结束了，释放  _drag
  void _disposeDrag() {
    _drag = null;
  }
}
